package org.model.modbus;

import net.wimpi.modbus.Modbus;
import net.wimpi.modbus.ModbusException;
import net.wimpi.modbus.ModbusIOException;
import net.wimpi.modbus.ModbusSlaveException;
import net.wimpi.modbus.io.ModbusTransaction;
import net.wimpi.modbus.msg.ExceptionResponse;
import net.wimpi.modbus.msg.ModbusRequest;
import net.wimpi.modbus.msg.ModbusResponse;
import org.io.IOException;
import org.util.SerialPort;

/**
 *
 * @author German Garcia
 */
public class GSMModbusSerialTransaction implements ModbusTransaction {
    private static int c_TransactionID = Modbus.DEFAULT_TRANSACTION_ID;


    //instance attributes and associations
    private GSMModbusRTUTransport m_IO;
    private ModbusRequest m_Request;
    private ModbusResponse m_Response;
    private boolean m_ValidityCheck = Modbus.DEFAULT_VALIDITYCHECK;
    private int m_Retries = Modbus.DEFAULT_RETRIES;
    private int m_TransDelayMS = Modbus.DEFAULT_TRANSMIT_DELAY;
    private SerialPort m_SerialCon;

    /**
    * Constructs a new <tt>ModbusSerialTransaction</tt>
    * instance.
    */
    public GSMModbusSerialTransaction() {
    }//constructor

    /**
    * Constructs a new <tt>ModbusSerialTransaction</tt>
    * instance with a given <tt>ModbusRequest</tt> to
    * be send when the transaction is executed.
    * <p>
    * @param request a <tt>ModbusRequest</tt> instance.
    */
    public GSMModbusSerialTransaction(ModbusRequest request) {
        setRequest(request);
    }//constructor


    /**
    * Constructs a new <tt>ModbusSerialTransaction</tt>
    * instance with a given <tt>ModbusRequest</tt> to
    * be send when the transaction is executed.
    * <p>
    * @param serialPort a <tt>TCPMasterConnection</tt> instance.
    */
    public GSMModbusSerialTransaction(SerialPort con, GSMModbusRTUTransport modbusTransport) throws IOException {
        setSerialPort(con, modbusTransport);
    }//constructor
    /**
    * Sets the port on which this <tt>ModbusTransaction</tt>
    * should be executed.<p>
    * <p>
    * @param con a <tt>SerialConnection</tt>.
    */
    public void setSerialPort(SerialPort con, GSMModbusRTUTransport modbusTransport) throws IOException {
        m_SerialCon = con;
        m_IO = modbusTransport;
        m_IO.setCommPort(m_SerialCon);
    }//setConnection

    public int getTransactionID() {
        return c_TransactionID;
    }//getTransactionID


    public void setRequest(ModbusRequest req) {
        m_Request = req;
        //m_Response = req.getResponse();
    }//setRequest

    public ModbusRequest getRequest() {
        return m_Request;
    }//getRequest

    public ModbusResponse getResponse() {
        return m_Response;
    }//getResponse

    public void setCheckingValidity(boolean b) {
        m_ValidityCheck = b;
    }//setCheckingValidity

    public boolean isCheckingValidity() {
        return m_ValidityCheck;
    }//isCheckingValidity

    public int getRetries() {
        return m_Retries;
    }//getRetries

    public void setRetries(int num) {
        m_Retries = num;
    }//setRetries

    /**
    * Get the TransDelayMS value.
    * @return the TransDelayMS value.
    */
    public int getTransDelayMS() {
        return m_TransDelayMS;
    }

    /**
    * Set the TransDelayMS value.
    * @param newTransDelayMS The new TransDelayMS value.
    */
    public void setTransDelayMS(int newTransDelayMS) {
        this.m_TransDelayMS = newTransDelayMS;
    }

    public void execute() throws ModbusIOException, ModbusSlaveException, ModbusException {
        //1. assert executeability
        assertExecutable();

        //3. write request, and read response,
        //   while holding the lock on the IO object
        synchronized (m_IO) {
            int tries = 0;
            boolean finished = false;
            do {
                try {
                    //write request message
                    m_IO.writeMessage(m_Request);
                    if (m_TransDelayMS > 0) {
                        Thread.sleep(m_TransDelayMS);
                    }
                    //read response message
                    m_Response = m_IO.readResponse();
                    
                    System.out.println("Received: " + m_Response.getHexMessage());                    
                    finished = true;
                    
                } catch (ModbusIOException e) {
                    if (++tries >= m_Retries) {
                        
                        System.err.println("Transaction could not be processed: " + e);
                        
                        throw e;
                    }
                    System.err.println("execute try " + tries + " error: " + e.getMessage());
                } catch (InterruptedException ie) {}
            } while (!finished);
        }

        //4. deal with exceptions
        if (m_Response instanceof ExceptionResponse) {
            throw new ModbusSlaveException( ((ExceptionResponse) m_Response).getExceptionCode());
        }

        if (isCheckingValidity()) {
            checkValidity();
        }
        //toggle the id
        toggleTransactionID();
    }//execute

    /**
    * Asserts if this <tt>ModbusTCPTransaction</tt> is
    * executable.
    *
    * @throws ModbusException if the transaction cannot be asserted.
    */
    private void assertExecutable() throws ModbusException {
        if (m_Request == null || m_SerialCon == null) {
            throw new ModbusException( "Assertion failed, transaction not executable" );
        }
    }//assertExecuteable

    /**
    * Checks the validity of the transaction, by
    * checking if the values of the response correspond
    * to the values of the request.
    *
    * @throws ModbusException if the transaction is not valid.
    */
    private void checkValidity() throws ModbusException {

    }//checkValidity

    /**
    * Toggles the transaction identifier, to ensure
    * that each transaction has a distinctive
    * identifier.<br>
    * When the maximum value of 65535 has been reached,
    * the identifiers will start from zero again.
    */
    private void toggleTransactionID() {
        if (isCheckingValidity()) {
            if (c_TransactionID == (Short.MAX_VALUE * 2)) {
                c_TransactionID = 0;
            } else {
                c_TransactionID++;
            }
        }
        m_Request.setTransactionID(getTransactionID());
    }//toggleTransactionID
}